package com.rsscollector.model;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.compass.core.*;

import com.rsscollector.model.vo.NewsGroup;
import com.rsscollector.model.vo.RssContent;
import com.rsscollector.model.vo.RssProvider;
import com.rsscollector.model.vo.RssSource;
import com.rsscollector.util.PersistenceUtil;
import com.rsscollector.util.StringUtil;

public class RssSourceFacade {

    private static Logger logger = Logger.getLogger(RssSourceFacade.class.getName());

    public static RssSource createRssSource(String url, String providerName) {
        EntityManager em = PersistenceUtil.getEntityManagerWithTX();

        List result = em.createQuery("SELECT s FROM RssSource s WHERE s.rssUrl = ?1").setParameter(1, url).getResultList();
        RssSource source;
        if (result.size() == 0) {
            source = new RssSource();
            source.setRssUrl(url);

            if (providerName != null) {
                RssProvider provider = getRssProviderByName(providerName);
                if (provider == null) provider = createRssProvider(providerName);
                source.setRssProvider(provider);
                provider.getRssSources().add(source);
                em.persist(provider);
            }

            em.persist(source);
        } else {
            source = (RssSource) result.get(0);
        }
        PersistenceUtil.endTransaction();
        return source;
    }

    public static List listRssSources(boolean active) {
        List result;
        EntityManager em = PersistenceUtil.getEntityManager();
        String whereClause = "";
        if (active) whereClause = " WHERE s.active = TRUE";
        result = em.createQuery("SELECT s FROM RssSource s"+whereClause).getResultList();
        return result;
    }

    public static RssSource getRssSource(Long rssSourceId) {
        RssSource result;
        EntityManager em = PersistenceUtil.getEntityManager();
        result = em.find(RssSource.class, rssSourceId);
        return result;
    }

    public static RssContent getNews(String newsId) {
        if (!StringUtil.isEmpty(newsId)) {
            return getNews(Long.parseLong(newsId));
        }
        return null;
    }
    public static RssContent getNews(Long newsId) {
        RssContent result;
        EntityManager em = PersistenceUtil.getEntityManager();
        result = em.find(RssContent.class, newsId);
        return result;
    }

    public static RssProvider createRssProvider(String providerName) {
        EntityManager em = PersistenceUtil.getEntityManagerWithTX();
        RssProvider provider = new RssProvider(providerName);
        em.persist(provider);
        PersistenceUtil.endTransaction();
        return provider;
    }

    public static List listRssProviders() {
        List result;
        EntityManager em = PersistenceUtil.getEntityManager();
        result = em.createQuery("SELECT p FROM RssProvider p").getResultList();
        return result;
    }

    public static RssProvider getRssProviderByName(String name) {
        EntityManager em = PersistenceUtil.getEntityManager();
        Query q = em.createQuery("SELECT p FROM RssProvider p WHERE p.name = ?1");
        q.setParameter(1, name);
        List providers = q.getResultList();
        RssProvider result = (RssProvider) (providers.size() > 0 ? providers
                .get(0) : null);
        return result;
    }
    public static RssContent getRssContent (String url) {
        EntityManager em = PersistenceUtil.getEntityManager();
        Query q = em.createQuery("SELECT c FROM RssContent c WHERE c.link = ?1");
        q.setParameter(1, url);
        RssContent result = null;
        try {
            result = (RssContent)q.getSingleResult();
        } catch (Exception ex) {
            result = null;
            logger.debug("Cannot find rsscontent with link " + url);
        }
        return result;

    }
    public static void updateNewsGroups(RssContent news) {
        double similarityThreshold = 0.2;
        logger.debug("BEGIN updateNewsGroups() for news.id=" + news.getId()+ ", news.title=" + news.getTitle());

        if (news.getGroup() == null) {
            // Begin a database transaction
            EntityManager em = PersistenceUtil.getEntityManagerWithTX();

            // Find a similar news
            RssContent similarNews = null;
            Compass compass = PersistenceUtil.getCompass();
            CompassSession session = compass.openSession();
            CompassTransaction tx = null;
            try {
                tx = session.beginTransaction();
                CompassQueryBuilder queryBuilder = session.queryBuilder();
                String keywords = collectKeywords(news);

                CompassHits hits = queryBuilder.queryString(keywords).toQuery().hits();
                logger.debug("Find a similar news, found " + hits.getLength());
                if (hits.getLength() > 0) {
                    for (int i =0; i < hits.getLength(); i++) {
                        RssContent hit = (RssContent) hits.data(i);
                        float score = hits.score(i);
                        logger.debug("Found Item " + i + ", score " + score + ", id=" + hit.getId());
                        RssContent item = getNews(hit.getId());
                        if (item != null && hit.getId().longValue() != news.getId().longValue()) {
                            logger.debug("Best Found Item " + i + ", score " + score + ", id=" + item.getId());
                            if (score >= similarityThreshold) {
                                similarNews = getNews(hit.getId());
                            }
                            break;
                        }
                    }
                }
                tx.commit();
            } catch (CompassException ce) {
                if (tx != null) tx.rollback();
            } finally {
                session.close();
            }

            // If a similar news found, two news will be put into same group
            if (similarNews != null) {
                logger.debug("Find a similar news id= " + similarNews.getId());
                // If there isn't a similar news or the similar news doesn't have a
                // news group, create a news group
                NewsGroup newsGroup;
                if (similarNews.getGroup() == null) {
                    logger.debug("No candidate newsgroup found, create a new group");
                    newsGroup = new NewsGroup();
                    similarNews.setGroup(newsGroup);
                    newsGroup.getNews().add(similarNews);
                } else {
                    //this is second news of this group
                    news.setFirstNewsOfGroup(false);
                    newsGroup = similarNews.getGroup();
                    logger.debug("Found newsgroup with id=" + similarNews.getGroup().getId());
                }

                // Update relationships between news and newsGroup
                news.setGroup(newsGroup);
                newsGroup.getNews().add(news);

                // Persist to database
                logger.debug("Update news and group to database");
                em.persist(news);
                em.persist(similarNews);
                em.persist(newsGroup);
            } else {
                logger.debug("No similar news found");
            }
            PersistenceUtil.endTransaction();
        }
        logger.debug("END updateNewsGroups()");
    }

    private static String collectKeywords(RssContent news) {
        String text = news.getTitle() + ". " + news.getDescriptionValue();
        String searchQuery = escapeLuceneSpecialChars(text);
        return searchQuery;
    }

    private static String escapeLuceneSpecialChars(String string) {
        String specialChars = "\\+-&|!(){}[]^\"~*?:";
        for (int i = 0; i < specialChars.length(); i++) {
            char c = specialChars.charAt(i);
            string = StringUtils.replace(string, String.valueOf(c), "\\" + c);
        }
        return string;
    }
}
